<?php

if (!defined('BASE_PATH'))
	exit('Restricted access');

/**
 * 数据库访问抽象类
 * 
 * 所有数据库驱动类由此类继承。
 * 此类中实现了数据库抽象层方法。
 * 
 * @author sandy
 * @package sFramework
 *
 */
abstract class Database {

	/**
	 * 连接资源标识符
	 * 
	 * @var resource
	 */
	protected $conn_id;

	/**
	 * 当前实例执行的查询次数
	 * 
	 * @var int
	 */
	protected $query_count = 0;

	/**
	 * 最后一次查询的标识符
	 * 
	 * @var resource
	 */
	protected $_cursor = null;

	/**
	 * 当前查询语句
	 * 
	 * @var string
	 */
	protected $_sql = '';

	/**
	 * 查询限制
	 *
	 * @var int
	 */
	protected $_limit = 0;

	/**
	 * 查询偏移量
	 *
	 * @var int
	 */
	protected $_offset = 0;

	/**
	 * sql排序语句
	 *
	 * @var int
	 */
	protected $_order_str = '';

	/**
	 * 调试开关
	 * 
	 * @var int
	 */
	protected $debug = 0;

	/**
	 * 执行过的查询语句记录
	 * 
	 * @var array
	 */
	protected $_log = null;

	/**
	 * 数据库错误信息
	 * 
	 * @var string
	 */
	protected $_error_msg = '';

	/* 以下为需要配置文件传入的参数 */
	protected $hostname;
	protected $port;
	protected $dbdriver;
	protected $username;
	protected $password;
	protected $database;
	protected $tableprefix = '';
	// 可选项目
	protected $char_set = 'utf8';
	protected $dbcollat = 'utf8_general_ci';
	protected $pconnect = FALSE;
	protected $autoinit = TRUE;

	public function __construct($config) {
		// 赋值配置信息
		if (is_array($config)) {
			foreach ($config as $key => $val) {
				$this->$key = trim($val);
			}
		}

		if ($this->autoinit == TRUE) {
			$this->initialize();
		}
	}

	final protected function initialize() {
		if (is_resource($this->conn_id) OR is_object($this->conn_id)) {
			return TRUE;
		}

		// 尝试进行连接
		($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect();

		// 选择配置文件文件中指定的数据库
		if ($this->database != '') {
			if ($this->db_select()) {
				// 设置数据库编码信息
				$this->db_set_charset($this->char_set, $this->dbcollat);
			}
		}

		if (($error = $this->get_error_msg())) {
			show_error($error);
			return FALSE;
		}

		return TRUE;
	}

	public function debug($level) {
		$this->debug = intval($level);
	}

	/**
	 * 执行过的查询语句记录
	 */
	public function get_log() {
		return $this->_log;
	}

	//---- 中间层访问接口

	/**
	 * 设置查询语句
	 * 
	 * @param string $sql
	 * @param int $offset
	 * @param int $limit
	 * @param string $prefix
	 */
	public function set_query($sql, $offset = 0, $limit = 0, $prefix = '#__') {
		$this->_sql = $this->replacePrefix($sql, $prefix);
		$this->_limit = (int) $limit;
		$this->_offset = (int) $offset;
	}

	abstract public function query();

	abstract public function get_all();

	abstract public function get_row();

	abstract public function get_one();

	abstract public function update($table, &$data, $where);

	abstract public function insert($table, &$data);


	//----  中间层访问接口	

	/**
	 * 获取插入数据的SQL语句信息
	 * 
	 * @param $data 以数据表中对应字段为键名的待插入数据键值对数组
	 */
	protected function get_insert_info($data) {
		reset($data);
		$fields = array();
		$values = array();
		$length = 1;
		if (key($data) === 0 && is_array($data[0])) {
			$length = count($data);
			foreach ($data as $_k => $_v) {
				foreach ($_v as $_f => $_fv) {
					$is_array = is_array($_fv);
					($_k == 0 && !$is_array) && $fields[] = $_f;
					!$is_array && $values[$_k][] = "'{$_fv}'";
				}
				$values[$_k] = '(' . implode(',', $values[$_k]) . ')';
			}
		} else {
			foreach ($data as $_k => $_v) {
				$is_array = is_array($_v);
				!$is_array && $fields[] = $_k;
				!$is_array && $values[] = "'{$_v}'";
			}
			$values = '(' . implode(',', $values) . ')';
		}
		$fields = '(' . implode(',', $fields) . ')';
		is_array($values) && $values = implode(',', $values);

		return compact('fields', 'values', 'length');
	}

	/**
	 * 获取错误信息
	 */
	protected function get_error_msg() {
		return $this->_error_msg;
	}

	/**
	 * This function replaces a string identifier <var>$prefix</var> with the
	 * string held is the <var>_table_prefix</var> class variable.
	 *
	 * @access public
	 * @param string The SQL query
	 * @param string The common table prefix
	 */
	protected function replacePrefix($sql, $prefix = '#__') {
		$sql = trim($sql);

		$escaped = false;
		$quoteChar = '';

		$n = strlen($sql);

		$startPos = 0;
		$literal = '';
		while ($startPos < $n) {
			$ip = strpos($sql, $prefix, $startPos);
			if ($ip === false) {
				break;
			}

			$j = strpos($sql, "'", $startPos);
			$k = strpos($sql, '"', $startPos);
			if (($k !== FALSE) && (($k < $j) || ($j === FALSE))) {
				$quoteChar = '"';
				$j = $k;
			} else {
				$quoteChar = "'";
			}

			if ($j === false) {
				$j = $n;
			}

			$literal .= str_replace($prefix, $this->tableprefix, substr($sql, $startPos, $j - $startPos));
			$startPos = $j;

			$j = $startPos + 1;

			if ($j >= $n) {
				break;
			}

			// quote comes first, find end of quote
			while (TRUE) {
				$k = strpos($sql, $quoteChar, $j);
				$escaped = false;
				if ($k === false) {
					break;
				}
				$l = $k - 1;
				while ($l >= 0 && $sql{$l} == '\\') {
					$l--;
					$escaped = !$escaped;
				}
				if ($escaped) {
					$j = $k + 1;
					continue;
				}
				break;
			}
			if ($k === FALSE) {
				// error in the query - no end quote; ignore it
				break;
			}
			$literal .= substr($sql, $startPos, $k - $startPos + 1);
			$startPos = $k + 1;
		}
		if ($startPos < $n) {
			$literal .= substr($sql, $startPos, $n - $startPos);
		}
		return $literal;
	}

	/*
	 * 此基类中实现抽象接口。
	 * 各驱动层需要实现特定的底层操作方法。
	 * 抽象接口对底层方法进行封装，
	 * 由此实现与数据库类型无关
	 */

	abstract protected function db_connect();

	abstract protected function db_pconnect();

	abstract protected function db_select();

	abstract protected function db_set_charset($char_set, $dbcollat);
}
